package com.lagou.edu.factory;

import com.lagou.edu.dao.AccountDao;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Set<Class<?>> serviceSet;
    private static Map<String,Object> nameMap = new HashMap<>();
    private static Map<Class<?>, String> class2NameMap = new HashMap();
    //private static Map<Class<?>,Object> classMap = new HashMap<>();


    static {
        createBeans();
        wareBeans();
        proxyBeans();
    }

    private static void createBeans() {
        //读取com.lagou.edu包下所有包含Service注解的包
        final String rootPackage = "com.lagou.edu";
        Reflections ref = new Reflections(rootPackage);
        serviceSet = ref.getTypesAnnotatedWith(Service.class);
        try {
            for (Class<?> c : serviceSet) {
                Service anno = c.getAnnotation(Service.class);
                String beanName = anno.Value();
                if (beanName.isEmpty()) {
                    //使用类名的全小写来当作bean name
                    Class<?>[] inters = c.getInterfaces();
                    if (inters.length > 0) {
                        beanName = getBeanName(inters[0].getName());
                        class2NameMap.put(inters[0], beanName);
                    } else {
                        beanName = getBeanName(c.getName());
                    }
                }
                Object bean = c.getDeclaredConstructor().newInstance();
                nameMap.put(beanName, bean);
                class2NameMap.put(c, beanName);
                //classMap.put(c, bean);
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static String getBeanName(String fullName) {
        String[] spt = fullName.split("\\.");
        String name = spt[spt.length - 1];
        if (Character.isLowerCase(name.charAt(0))) {
            return name;
        } else {
            return (new StringBuilder()).append(Character.toLowerCase(name.charAt(0)))
                    .append(name.substring(1)).toString();
        }
    }

    private static void wareBeans() {
        try {
            for (Class<?> c : serviceSet) {
                Object obj = getBean(c);
                /*if (obj == null) {
                    Class<?>[] inters = c.getInterfaces();
                    if (inters.length > 0) {
                        obj = getBean(inters[0]);
                    }
                }*/
                if (obj == null) {
                    continue;
                }
                Field[] fields = c.getDeclaredFields();
                if (fields != null) {
                    for (Field field : fields) {
                        field.setAccessible(true);
                        Autoware wareAnno = field.getAnnotation(Autoware.class);
                        if (wareAnno != null) {
                            Class<?> fieldClass = field.getType();
                            Object fieldValue = getBean(fieldClass);
                            field.set(obj, fieldClass.cast(fieldValue));
                        }
                    }
                }
            }
            System.out.println("Autowared finished!");
        } catch(IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    private static void proxyBeans() {
        try {
            for (Class<?> c : serviceSet) {
                Transaction tranAnno = c.getAnnotation(Transaction.class);
                if (tranAnno != null) {
                    ProxyFactory pf = (ProxyFactory)getBean("proxyFactory");
                    Object rawObj = getBean(c);
                    if (rawObj == null) {
                        continue;
                    }
                    Object proxyObj = pf.getJdkProxy(rawObj);
                    String beanName = class2NameMap.get(c);
                    nameMap.put(beanName, proxyObj);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return nameMap.get(id);
    }

    public static  Object getBean(Class<?> clazz) {
        if (class2NameMap.containsKey(clazz)) {
            return nameMap.get(class2NameMap.get(clazz));
        } else {
            return null;
        }
    }

}
